#include "../include/EchoParser.h"

/*
Class Definitions for EchoParser
*/

EchoParser::EchoParser()
{
	//
	m_document = NULL;
	m_visitor = new EchoParserVisitor();
	m_visitor->setParser(this);
	m_segments = new std::vector<Segment*>();
	m_beatObjects = new std::vector<BeatObject*>();
	m_scalar_attr = new std::map<string, float>();
	m_vector_attr = new std::map<string, float*>();
	m_section_starts = new std::vector<float>();
	m_section_durations = new std::vector<float>();
	m_tatums = NULL;
}

EchoParser::~EchoParser()
{
	//
	delete m_segments;
	free(m_tatums);
}

int EchoParser::readXmlFile(const char* path)
{
	if(m_document != NULL)
	{
		delete m_document;
	}
	m_document = new TiXmlDocument( path );
	bool loadOkay = m_document->LoadFile();
	if(loadOkay)
	{
		printf("\n%s:\n", path);
		return 0;
	}
	else
	{
		printf("Failed to load file \"%s\"\n", path);
		return -1;
	}
}

std::vector<Segment*>* EchoParser::getSegments()
{
	return m_segments;
}

int EchoParser::getAverageTatumsPerSegment()
{
	int sum = 0;
	for(unsigned int i = 0; i < m_segments->size(); i++)
	{
		sum += ((*m_segments)[i])->getNumTatums();
	}
	
	return ( (int) ( sum / m_segments->size()));
}

std::vector<float>* EchoParser::getSectionStarts()
{
	return m_section_starts;
}

std::vector<float>* EchoParser::getSectionDurations()
{
	return m_section_durations;
}

std::vector<BeatObject*>* EchoParser::getBeatObjects(int difficulty)
{
	//Allocate the list we're going to return
	std::vector<BeatObject*>* beats = new std::vector<BeatObject*>();
	std::vector<float>* starts = getSectionStarts();
	float* averages;
	averages = (float*)malloc(sizeof(float)*starts->size());
	float normalizing_constant = 1.3; //higher value -> more beat objects
	float glAvgShift = 10; //higher value -> fewer beat objects
	float shift1 = 1.0;
	float shift2 = 2.0;
	memset(&(averages[0]), 0, sizeof(float) * starts->size());
	
	
	//Calculate loudness average per section & store in array
	for(unsigned int i = 0; i < starts->size(); i++)
	{
		
		//Check through all segments to determine which get added
		float local_average = 0.0;
		int num_section_segments = 0;
		for(unsigned int j = 0; j < m_segments->size(); j++)
		{
			//Get segment
			Segment* seg = (*m_segments)[j];
			
			if(i == starts->size() - 1)
			{
				if(seg->getStart() >= (*starts)[i])
				{
					num_section_segments++;
					local_average += seg->getLoudnessMax();
					seg->setSectionId(i);
				}
			}
			else if( (seg->getStart() >= (*starts)[i]) && (seg->getStart() < (*starts)[i + 1]) )
			{
				num_section_segments++;
				local_average += seg->getLoudnessMax();
				seg->setSectionId(i);
			}
		}
		
		local_average = local_average / num_section_segments;
		averages[i] = local_average;
	}
	
	for(unsigned int i = 0; i < m_segments->size(); i++)
	{
		Segment* seg = (*m_segments)[i];
		float threshold;
		float globalAvg = getScalarAttr("loudnessMaxMean");
		float localAvg = averages[seg->getSectionId()];
		
		if(localAvg < globalAvg)
		{
			//threshold =  ( (globalAvg + glAvgShift) - localAvg ) / normalizing_constant;
			threshold = 0;
		}
		else
		{
			threshold = - ( (globalAvg - glAvgShift) - localAvg) / normalizing_constant;
		}
		//printf("Global Avg: %f, Local Avg: %f, Threshold: %f\n", getScalarAttr("loudnessMaxMean"), averages[seg->getSectionId()], threshold);
		if(seg->getLoudnessMax() >= localAvg)//+threshold)
		{
			/*
			Okay, so this segment is loud enough. Let's get all of the
			tatums within this segment
			*/
			//printf("Adding Segment which has a loudnessMax of %f\n", seg->getLoudnessMax() );
			for(int r = 0; r < seg->getNumTatums(); r++)
			{
				//Get tatum
				float tatum = seg->getTatum(r);
				
				//Assign an arbitrary letter (for now)
				char c;
				
				//srand(time(NULL));
				int random = rand() % 100;
				if(random >= 0 && random < 25)
				{
					c = 'a';
				}
				else if(random < 50)
				{
					c = 'w';
				}
				else if(random < 75)
				{
					c = 's';
				}
				else
				{
					c = 'd';
				}
				
				//Create a beat object
				BeatObject* bo = new BeatObject();
				bo->m_tatum = tatum;
				bo->m_char = c;
				
				beats->push_back(bo);
			}
		}
	}
	
	return beats;
}

void EchoParser::process()
{
	printf("Process XML data...\n");
	assert(m_visitor);
	m_document->Accept( m_visitor );
	printf("Done processing XML data.\n");
}

void EchoParser::print()
{
	unsigned int i;
	for(i = 0; i < m_segments->size(); i++)
	{
		Segment* seg = (*m_segments)[i];
		seg->print();
	}
}

int EchoParser::getNumTatums()
{
	return getScalarAttr("numTatums");
}

float* EchoParser::getTatums()
{
	return m_tatums;
}

float EchoParser::getTatum(int i)
{
	return m_tatums[i];
}

float EchoParser::getScalarAttr(string key)
{
	return (*m_scalar_attr)[key];
}

float* EchoParser::getVectorAttr(string key)
{
	return (*m_vector_attr)[key];
}

void EchoParser::setScalarAttr(string key, float value)
{
	(*m_scalar_attr)[key] = value;
}

void EchoParser::setVectorAttr(string key, float* value)
{
	(*m_vector_attr)[key] = value;
}

void EchoParser::setTatums(float* f)
{
	m_tatums = f;
}

void EchoParser::setTatum(int i, float tatum)
{
	m_tatums[i] = tatum;
}


/////////////////////////////////////////////////////////
// EchoParserVisitor Definition//////////////////////////
/////////////////////////////////////////////////////////

EchoParserVisitor::EchoParserVisitor()
{
	m_nextSegmentId = 0;
	m_currentSegment = NULL;
}

bool EchoParserVisitor::VisitEnter(const TiXmlDocument &)
{
	//printf("Visiting document\n");
	
	return true;
}

bool EchoParserVisitor::VisitExit(const TiXmlDocument &)
{
	//printf("Exiting document...\n");
	
	return true;
}

bool EchoParserVisitor::VisitEnter(const TiXmlElement & e, const TiXmlAttribute* a)
{
	const char* elementName = e.Value();
	//printf("Visiting element: #%s#\n", elementName);
	
	if(strcmp(elementName, "Segment") == 0)
	{
		//We have arrived at a <Segment ..> tag
		m_isVisitingSegment = true;
		
		/*
		Create a Segment object for this tag, and add it to vector
		maintained in the main EchoParser class
		*/
		
		//First we get the vector of Segments
		std::vector<Segment*>* vec = m_parser->getSegments();
		
		//Create object
		Segment* seg = new Segment(m_nextSegmentId);
		m_currentSegment = seg;
		
		/*
		Now get the start and duration attributes and store
		them as floats (so convert from C string)
		
		NOTE: This method works, except there is a slight precision error.
		For example, if the XML doc says "start = '166.20548'", then after this is read into memory, the FPU assigns it the value of 166.205475.
		Let's just keep this in mind incase this becomes a problem.
		*/
		
		float start, duration;
		char* startStr = (char*)e.Attribute("start");
		sscanf(startStr, "%f", &start);
		//printf("Float start: %f\n", start);
		
		char* durStr = (char*)e.Attribute("duration");
		sscanf(durStr, "%f", &duration);
		//printf("Float duration: %f\n", duration);
		
		seg->setStart(start);
		seg->setDuration(duration);
		
		//Have the segment identify which tatums pertain to it
		seg->processTatums(m_parser->getTatums(), m_parser->getNumTatums() );
		
		//Add to end of vector
		vec->push_back(seg);
		//printf("%d segments in list\n", vec->size());
		
		
		//Increment the m_nextSegmentId so that the next segment gets a new ID
		m_nextSegmentId++;
	}
	else if((strcmp(elementName, "Tags") == 0 ) && m_currentSegment != NULL)
	{
		//We've arrived at a <Tags> tag within a segment tag
		
		//First get the singular values
		float loudnessBegin = str2float(e.Attribute("loudnessBegin"));
		float loudnessMax = str2float(e.Attribute("loudnessMax"));
		float loudnessEnd = str2float(e.Attribute("loudnessEnd"));
		float timeLoudnessMax = str2float(e.Attribute("timeLoudnessMax"));
	
		/*	
		std::cout << "LoudBegin: " << loudnessBegin << " Loud Max: " << loudnessMax << "LoudnessEnd: " << loudnessEnd << " loudnessMax: " << timeLoudnessMax << std::endl;
		*/
		
		//Now lets get the multiple values (pitch and timbre each have values for 12 voices
		
		//Pitches
		float p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12;
		sscanf(e.Attribute("pitches"), "%f %f %f %f %f %f %f %f %f %f %f %f", &p1, &p2, &p3, &p4, &p5, &p6, &p7, &p8, &p9, &p10, &p11, &p12);
		//printf("Pitches: %f %f %f %f %f %f %f %f %f %f %f %f\n", p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12);
		
		
		//Timbre Coefficients
		float t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12;
		sscanf(e.Attribute("timbreCoeff"), "%f %f %f %f %f %f %f %f %f %f %f %f", &t1, &t2, &t3, &t4, &t5, &t6, &t7, &t8, &t9, &t10, &t11, &t12);
		//printf("Timbre Coeffs: %f %f %f %f %f %f %f %f %f %f %f %f\n", t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12);
		
		//Assign these values to the current segment
		
		m_currentSegment->setLoudnessBegin(loudnessBegin);
		m_currentSegment->setLoudnessMax(loudnessMax);
		m_currentSegment->setLoudnessEnd(loudnessEnd);
		m_currentSegment->setTimeLoudnessMax(timeLoudnessMax);
		
		m_currentSegment->setPitch(0, p1);
		m_currentSegment->setPitch(1, p2);
		m_currentSegment->setPitch(2, p3);
		m_currentSegment->setPitch(3, p4);
		m_currentSegment->setPitch(4, p5);
		m_currentSegment->setPitch(5, p6);
		m_currentSegment->setPitch(6, p7);
		m_currentSegment->setPitch(7, p8);
		m_currentSegment->setPitch(8, p9);
		m_currentSegment->setPitch(9, p10);
		m_currentSegment->setPitch(10, p11);
		m_currentSegment->setPitch(11, p12);
		
		m_currentSegment->setTimbre(0, t1);
		m_currentSegment->setTimbre(1, t2);
		m_currentSegment->setTimbre(2, t3);
		m_currentSegment->setTimbre(3, t4);
		m_currentSegment->setTimbre(4, t5);
		m_currentSegment->setTimbre(5, t6);
		m_currentSegment->setTimbre(6, t7);
		m_currentSegment->setTimbre(7, t8);
		m_currentSegment->setTimbre(8, t9);
		m_currentSegment->setTimbre(9, t10);
		m_currentSegment->setTimbre(10, t11);
		m_currentSegment->setTimbre(11, t12);
		
		
	}
	else if( (strcmp(elementName, "Track") == 0) )
	{
		//Set scalar attributes
		m_parser->setScalarAttr( "duration", str2float(e.Attribute("duration")));
		
		m_parser->setScalarAttr("usableDuration", str2float(e.Attribute("usableDuration")));
		
		
	}
	else if( (strcmp(elementName, "Tags") == 0) )
	{
		m_parser->setScalarAttr("loudnessDynamicsVariance", str2float(e.Attribute("loudnessDynamicsVariance")));
		
		m_parser->setScalarAttr("endOfFadeIn", str2float(e.Attribute("endOfFadeIn")));
		
		m_parser->setScalarAttr("loudnessBeginVariance", str2float(e.Attribute("loudnessBeginVariance")));
		
		m_parser->setScalarAttr("loudnessBeginMean", str2float(e.Attribute("loudnessBeginMean")));
		
		m_parser->setScalarAttr("beatVariance", str2float(e.Attribute("beatVariance")));
		
		m_parser->setScalarAttr("loudnessDynamicsMean", str2float(e.Attribute("loudnessDynamicsMean")));
		
		m_parser->setScalarAttr("timeSignatureStability", str2float(e.Attribute("timeSignatureStability")));
		
		m_parser->setScalarAttr("loudnessMaxVariance", str2float(e.Attribute("loudnessMaxVariance")));
		
		m_parser->setScalarAttr("loudnessMaxMean", str2float(e.Attribute("loudnessMaxMean")));
		
		m_parser->setScalarAttr("segmentDurationVariance", str2float(e.Attribute("segmentDurationVariance")));
		
		m_parser->setScalarAttr("sizeTimbre", str2float(e.Attribute("sizeTimbre")));
		
		m_parser->setScalarAttr("startOfFadeOut", str2float(e.Attribute("startOfFadeOut")));
		
		m_parser->setScalarAttr("tatum", str2float(e.Attribute("tatum")));
		
		m_parser->setScalarAttr("timeLoudnessMaxMean", str2float(e.Attribute("timeLoudnessMaxMean")));
		
		m_parser->setScalarAttr("numTatums", str2float(e.Attribute("numTatums")));
		
		m_parser->setScalarAttr("tempoConfidence", str2float(e.Attribute("tempoConfidence")));
		
		m_parser->setScalarAttr("loudness", str2float(e.Attribute("loudness")));
		
		m_parser->setScalarAttr("tempo", str2float(e.Attribute("tempo")));
		
		m_parser->setScalarAttr("segmentDurationMean", str2float(e.Attribute("segmentDurationMean")));
		
		m_parser->setScalarAttr("numSections", str2float(e.Attribute("numSections")));
		
		m_parser->setScalarAttr("tatumConfidence", str2float(e.Attribute("tatumConfidence")));
		
		m_parser->setScalarAttr("timeSignature", str2float(e.Attribute("timeSignature")));
		
		m_parser->setScalarAttr("numBeats", str2float(e.Attribute("numBeats")));
		
		m_parser->setScalarAttr("sizePitches", str2float(e.Attribute("sizePitches")));
		
		m_parser->setScalarAttr("numSegments", str2float(e.Attribute("numSegments")));
		
		m_parser->setScalarAttr("numTatumsPerBeat", str2float(e.Attribute("numTatumsPerBeat")));
		
		
		//Set vector attributes
		
		//Timbre means
		float* t = (float*)malloc( sizeof(float) * 12);
		sscanf(e.Attribute("timbreMean"), "%f %f %f %f %f %f %f %f %f %f %f %f", &t[0], &t[1], &t[2], &t[3], &t[4], &t[5], &t[6], &t[7], &t[8], &t[9], &t[10], &t[11]);
		m_parser->setVectorAttr("timbreMean", t );
		
		//timbre Variance
		t = (float*)malloc( sizeof(float) * 12);
		sscanf(e.Attribute("timbreVariance"), "%f %f %f %f %f %f %f %f %f %f %f %f", &t[0], &t[1], &t[2], &t[3], &t[4], &t[5], &t[6], &t[7], &t[8], &t[9], &t[10], &t[11]);
		m_parser->setVectorAttr("timbreVariance", t );
		
		//pitchMean
		t = (float*)malloc( sizeof(float) * 12);
		sscanf(e.Attribute("pitchMean"), "%f %f %f %f %f %f %f %f %f %f %f %f", &t[0], &t[1], &t[2], &t[3], &t[4], &t[5], &t[6], &t[7], &t[8], &t[9], &t[10], &t[11]);
		m_parser->setVectorAttr("pitchMean", t );
		
		//pitchVariance
		t = (float*)malloc( sizeof(float) * 12);
		sscanf(e.Attribute("pitchVariance"), "%f %f %f %f %f %f %f %f %f %f %f %f", &t[0], &t[1], &t[2], &t[3], &t[4], &t[5], &t[6], &t[7], &t[8], &t[9], &t[10], &t[11]);
		m_parser->setVectorAttr("pitchVariance", t );
		
	}
	else if( (strcmp(elementName, "Tatums") == 0))
	{
		//First extract raw tatum char* data
		char* tatumText = (char*)e.GetText();
		const char* token;
		
		//Allocate memory for the tatums data
		m_parser->setTatums ((float*)malloc(sizeof(float) * m_parser->getNumTatums()) );
		printf("NumTatums: %d\n", m_parser->getNumTatums());
		token = strtok(tatumText, " ");
		int index = 0;
		while(token != NULL)
		{
			m_parser->setTatum(index, str2float(token));
			token = strtok(NULL, " ");
			index++;
		}
	}
	else if( (strcmp(elementName, "Section") == 0))
	{
		//
		float start, duration;
		sscanf(e.Attribute("start"), "%f", &start);
		sscanf(e.Attribute("duration"), "%f", &duration);
		
		std::vector<float>* starts = m_parser->getSectionStarts();
		starts->push_back(start);
		
		std::vector<float>* durations = m_parser->getSectionDurations();
		durations->push_back(duration);
	}
	
	return true;
}

bool EchoParserVisitor::VisitExit(const TiXmlElement & e)
{
	//printf("VisitExit element\n");
	const char* elementName = e.Value();
	
	if(strcmp(elementName, "Segment"))
	{
		m_isVisitingSegment = false;
		m_currentSegment = NULL;
	}
	return true;
}

bool EchoParserVisitor::isVisitingSegment()
{
	return (m_isVisitingSegment == true);
}

void EchoParserVisitor::setParser(EchoParser* p)
{
	m_parser = p;
}

float EchoParserVisitor::str2float(const char* str)
{
	float f;
	sscanf(str, "%f", &f);
	return f;
}
